
(function () {
    var IonicU9Module = angular.module('ionicU9', ['ionic']);

    var TIPS_TPL =
        '<div class="popup-container u9-tips-container" ng-class="cssClass">' +
            '<div class="popup u9-tips">' +
                '<div class="u9-tips-wrapper">' +
                    '<div class="popup-body" ng-class="{dismiss:!dismissable}">' +
                '</div>' +
                '<i class="icon ion-ios-close-empty icon-close-right" ng-if="!dismissable" ng-click="buttonTapped($event)"></i>' +
                '</div>' +
            '</div>' +
        '</div>';
    IonicU9Module.factory('ionicU9Tips', [
        '$ionicTemplateLoader',
        '$q',
        '$timeout',
        '$rootScope',
        '$ionicBody',
        '$compile',
        '$ionicPlatform',
        'IONIC_BACK_PRIORITY',
        function($ionicTemplateLoader, $q, $timeout, $rootScope, $ionicBody, $compile, $ionicPlatform, IONIC_BACK_PRIORITY) {

            var config = {
                stackPushDelay: 75
            };
            var popupStack = [];

            var ionicU9Tips = {
                show: showPopup,

                _createPopup: createPopup,
                _popupStack: popupStack
            };

            return ionicU9Tips;

            function createPopup(options) {
                options = angular.extend({
                    scope: null
                }, options || {});

                var self = {};
                self.scope = (options.scope || $rootScope).$new();
                self.element = angular.element(TIPS_TPL);
                self.responseDeferred = $q.defer();

                $ionicBody.get().appendChild(self.element[0]);
                $compile(self.element)(self.scope);

                var cssClass = options.cssClass || '';
                if (options.position === 'top') {
                    cssClass += ' u9-tips-top';
                } else if (options.position === 'bottom') {
                    cssClass += ' u9-tips-bottom';
                }
                angular.extend(self.scope, {
                    cssClass: cssClass,
                    dismissable: !!options.dismissable,
                    buttonTapped: function(event) {
                        event = event.originalEvent || event; //jquery events

                        if (!event.defaultPrevented) {
                            self.responseDeferred.resolve(true);
                        }
                    }
                });

                $q.when(
                    options.templateUrl ?
                        $ionicTemplateLoader.load(options.templateUrl) :
                        (options.template || options.content || '')
                ).then(function(template) {
                    var popupBody = angular.element(self.element[0].querySelector('.popup-body'));
                    if (template) {
                        popupBody.html(template);
                        $compile(popupBody.contents())(self.scope);
                    } else {
                        popupBody.remove();
                    }
                });

                self.show = function() {
                    if (self.isShown || self.removed) return;

                    self.isShown = true;
                    ionic.requestAnimationFrame(function() {
                        //if hidden while waiting for raf, don't show
                        if (!self.isShown) return;

                        self.element.removeClass('popup-hidden');
                        self.element.addClass('popup-showing active');
                    });
                };

                self.hide = function(callback) {
                    callback = callback || noop;
                    if (!self.isShown) return callback();

                    self.isShown = false;
                    self.element.removeClass('active');
                    self.element.addClass('popup-hidden');
                    $timeout(callback, 250, false);
                };

                self.remove = function() {
                    if (self.removed) return;

                    self.hide(function() {
                        self.element.remove();
                        self.scope.$destroy();
                    });

                    self.removed = true;
                };

                return self;
            }

            function onHardwareBackButton() {
                var last = popupStack[popupStack.length - 1];
                last && last.responseDeferred.resolve();
            }

            function showPopup(options) {
                var popup = ionicU9Tips._createPopup(options);
                var showDelay = 0;

                if (popupStack.length > 0) {
                    popupStack[popupStack.length - 1].hide();
                    showDelay = config.stackPushDelay;
                } else {
                    //Add popup-open & backdrop if this is first popup
                    $ionicBody.addClass('popup-open');
                    //only show the backdrop on the first popup
                    ionicU9Tips._backButtonActionDone = $ionicPlatform.registerBackButtonAction(
                        onHardwareBackButton,
                        IONIC_BACK_PRIORITY.popup
                    );
                }

                // Expose a 'close' method on the returned promise
                popup.responseDeferred.promise.close = function popupClose(result) {
                    if (!popup.removed) popup.responseDeferred.resolve(result);
                };
                //DEPRECATED: notify the promise with an object with a close method
                popup.responseDeferred.notify({ close: popup.responseDeferred.close });

                doShow();

                return popup.responseDeferred.promise;

                function doShow() {
                    popupStack.push(popup);
                    $timeout(function () {
                        popup.show();
                        if (options.dismissable) {
                            $timeout(function () {
                                popup.responseDeferred.resolve(true);
                            }, options.duration || 3000);
                        }
                    }, showDelay, false);

                    popup.responseDeferred.promise.then(function(result) {
                        var index = popupStack.indexOf(popup);
                        if (index !== -1) {
                            popupStack.splice(index, 1);
                        }

                        if (popupStack.length > 0) {
                            popupStack[popupStack.length - 1].show();
                        } else {
                            //Remove popup-open & backdrop if this is last popup
                            $timeout(function() {
                                // wait to remove this due to a 300ms delay native
                                // click which would trigging whatever was underneath this
                                if (!popupStack.length) {
                                    $ionicBody.removeClass('popup-open');
                                }
                            }, 400, false);
                            (ionicU9Tips._backButtonActionDone || noop)();
                        }

                        popup.remove();

                        return result;
                    });
                }
            }
        }]);

    IonicU9Module.factory('ionicU9ActionSheet', [
        '$rootScope',
        '$compile',
        '$animate',
        '$q',
        '$timeout',
        '$ionicTemplateLoader',
        '$ionicPlatform',
        '$ionicBody',
        'IONIC_BACK_PRIORITY',
        function($rootScope, $compile, $animate, $q, $timeout, $ionicTemplateLoader, $ionicPlatform, $ionicBody, IONIC_BACK_PRIORITY) {

            return {
                show: actionSheet
            };

            function actionSheet(opts) {
                var scope = opts.scope && opts.scope.$new() || $rootScope.$new(true);

                angular.extend(scope, {
                    cancel: angular.noop,
                    buttonClicked: angular.noop,
                    $deregisterBackButton: angular.noop,
                    cancelOnStateChange: true
                }, opts || {});

                // Compile the template
                var element = scope.element = $compile('<ion-u9-action-sheet ng-class="cssClass"></ion-u9-action-sheet>')(scope);

                $q.when(
                    opts.templateUrl ?
                        $ionicTemplateLoader.load(opts.templateUrl) :
                        (opts.template || opts.content || '')
                ).then(function(template) {
                        var popupBody = angular.element(element[0].querySelector('.u9-sheet-body'));
                        if (template) {
                            popupBody.html(template);
                            $compile(popupBody.contents())(scope);
                        } else {
                            popupBody.remove();
                        }
                    });

                // Grab the sheet element for animation
                var sheetEl = angular.element(element[0].querySelector('.action-sheet-wrapper'));

                var stateChangeListenDone = scope.cancelOnStateChange ?
                    $rootScope.$on('$stateChangeSuccess', function() { scope.cancel(); }) :
                    angular.noop;

                // removes the actionSheet from the screen
                scope.removeSheet = function(done) {
                    if (scope.removed) return;

                    scope.removed = true;
                    sheetEl.removeClass('action-sheet-up');
                    $timeout(function() {
                        // wait to remove this due to a 300ms delay native
                        // click which would trigging whatever was underneath this
                        $ionicBody.removeClass('action-sheet-open');
                    }, 400);
                    scope.$deregisterBackButton();
                    stateChangeListenDone();

                    $animate.removeClass(element, 'active').then(function() {
                        scope.$destroy();
                        element.remove();
                        // scope.cancel.$scope is defined near the bottom
                        scope.cancel.$scope = sheetEl = null;
                        (done || angular.noop)();
                    });
                };

                scope.showSheet = function(done) {
                    if (scope.removed) return;

                    $ionicBody.append(element)
                        .addClass('action-sheet-open');

                    $animate.addClass(element, 'active').then(function() {
                        if (scope.removed) return;
                        (done || angular.noop)();
                    });
                    $timeout(function() {
                        if (scope.removed) return;
                        sheetEl.addClass('action-sheet-up');
                    }, 20, false);
                };

                // registerBackButtonAction returns a callback to deregister the action
                scope.$deregisterBackButton = $ionicPlatform.registerBackButtonAction(
                    function() {
                        $timeout(scope.cancel);
                    },
                    IONIC_BACK_PRIORITY.actionSheet
                );

                scope.cancel = function() {
                    scope.removeSheet(opts.cancel);
                };

                scope.buttonClicked = function(index) {
                    if (opts.buttonClicked(index) === true) {
                        scope.removeSheet();
                    }
                };

                scope.showSheet();
                scope.cancel.$scope = scope;

                return scope.cancel;
            }
        }]);
    IonicU9Module.directive('ionU9ActionSheet', ['$document', function($document) {
        return {
            restrict: 'E',
            scope: true,
            replace: true,
            link: function($scope, $element) {

                var keyUp = function(e) {
                    if (e.which == 27) {
                        $scope.cancel();
                        $scope.$apply();
                    }
                };

                var backdropClick = function(e) {
                    if (e.target == $element[0]) {
                        $scope.cancel();
                        $scope.$apply();
                    }
                };
                $scope.$on('$destroy', function() {
                    $element.remove();
                    $document.unbind('keyup', keyUp);
                });

                $document.bind('keyup', keyUp);
                $element.bind('click', backdropClick);
            },
            template:
            '<div class="action-sheet-backdrop">' +
                '<div class="action-sheet-wrapper">' +
                    '<div class="u9-sheet">' +
                        '<div class="u9-sheet-title">' +
                            '<button class="button" ng-click="buttonClicked(0)">取消</button>' +
                            '<button class="button" ng-click="buttonClicked(1)">确认</button>' +
                        '</div>' +
                        '<div class="u9-sheet-body">' +
                        '</div>' +
                    '</div>' +
                '</div>' +
            '</div>'
        };
    }]);

    IonicU9Module.directive('scrollSelector', ['$timeout', '$ionicScrollDelegate', function($timeout, $ionicScrollDelegate) {
        return {
            restrict: 'E',
            scope: {
                list: '=',
                selected: '=',
                displayField: '@',
                cssClass: '@'
            },
            replace: true,
            template:
            '<div class="scroll-selector" ng-class="cssClass">' +
                '<div class="select-box">' +
                '</div>' +
                '<ion-scroll has-bouncing="true" class="scroll-selector-content" scrollbar-y="false" on-scroll="onScroll()">' +
                    '<ion-list>' +
                        '<ion-item></ion-item>' +
                        '<ion-item ng-repeat="t in list">{{t[displayField]}}</ion-item>' +
                        '<ion-item></ion-item>' +
                    '</ion-list>' +
                '</ion-scroll>' +
            '</div>',
            compile: function(element, attr) {
                var ionScroll = element.find('ion-scroll');

                angular.forEach({
                    'delegate-handle': attr.delegateHandle
                }, function(value, name) {
                    if (angular.isDefined(value)) {
                        ionScroll.attr(name, value);
                    }
                });

                return { pre: prelink };

                function prelink($scope, $element, $attr) {
                    var index = 0;
                    var timeOut = null;
                    var height = 34;

                    $timeout(init);

                    $scope.onScroll = function () {
                        scrolling();
                        if (timeOut) {
                            $timeout.cancel(timeOut);
                        }
                        timeOut = $timeout(scrollFinished, 260);
                    };

                    function init () {
                        var selected = $scope.selected,
                            len = $scope.list.length;
                        for (var i = 0; i < len; i++) {
                            if ($scope.list[i][$attr.valueField] === selected) {
                                index = i;
                                break;
                            }
                        }
                        var scrollCtrl = $ionicScrollDelegate.$getByHandle($attr.delegateHandle);
                        scrollCtrl.scrollTo(0, index * height);
                    }
                    function scrolling () {
                        var scrollCtrl = $ionicScrollDelegate.$getByHandle($attr.delegateHandle),
                            pos = scrollCtrl.getScrollPosition(),
                            scrollView = scrollCtrl.getScrollView();
                        if (!pos) {
                            return;
                        }
                        if (pos.top < 0 || pos.top > scrollView.__maxScrollTop) {
                            return;
                        }
                        var tmpIndex = parseInt(pos.top / height + 0.5) + 1;
                        var len = $scope.list.length + 2;
                        if (tmpIndex < 0) {
                            tmpIndex = 0;
                        }
                        if (tmpIndex >= len) {
                            tmpIndex = len - 1;
                        }
                        if (index === tmpIndex) {
                            return;
                        }
                        index = tmpIndex;
                        select();
                    }
                    function scrollFinished () {
                        var scrollCtrl = $ionicScrollDelegate.$getByHandle($attr.delegateHandle),
                            pos = scrollCtrl.getScrollPosition(),
                            scrollView = scrollCtrl.getScrollView();
                        if (!pos) {
                            return;
                        }
                        if (pos.top < 0 || pos.top > scrollView.__maxScrollTop) {
                            return;
                        }
                        if (pos.top % height !== 0) {
                            var index = parseInt(pos.top / height + 0.5);
                            scrollCtrl.scrollTo(0, index * height, true);
                        }
                    }
                    function select() {
                        var ionList = $element.find('ion-item'),
                            el;
                        angular.forEach(ionList, function (ionItem, i) {
                            el = angular.element(ionItem);
                            if (i === index) {
                                el.addClass('active');
                            } else {
                                el.removeClass('active');
                            }
                        });

                        var tmpIndex = index - 1;
                        if (tmpIndex < 0) {
                            tmpIndex = 0;
                        }
                        if (tmpIndex >= $scope.list.length) {
                            tmpIndex = $scope.list.length - 1;
                        }
                        $scope.$apply(function() {
                            $scope.selected = $scope.list[tmpIndex][$attr.valueField];
                        });
                    }
                }
            }
        };
    }]);

    IonicU9Module.directive('chatScroll', ['$timeout', '$ionicScrollDelegate', function($timeout, $ionicScrollDelegate) {
        return {
            restrict: 'E',
            scope: {
                quickCambers: '=',
                activeCamber: '='
            },
            transclude: true,
            replace: true,
            template:
            '<div class="chat-scroll">' +
                '<ion-scroll scrollbar-y="false" has-bouncing="true" on-scroll="onScroll()">' +
                    '<div class="chat-scroll-wrapper" ng-transclude></div>' +
                '</ion-scroll>' +
                '<div class="quick-bar">' +
                    '<ion-scroll scrollbar-y="false" has-bouncing="true ">' +
                        '<div class="quick-item" ng-repeat="quickCamber in quickCambers" ' +
                            'ng-class="{active:quickCamber===activeCamber}" ng-click="setActiveCamber(quickCamber)">' +
                        '{{quickCamber}}' +
                    '</ion-scroll>' +
                '</div>' +
                '</div>' +
            '</div>',
            compile: function(element, attr) {
                var ionScroll = angular.element(element[0].children[0]);

                angular.forEach({
                    'delegate-handle': attr.delegateHandle
                }, function(value, name) {
                    if (angular.isDefined(value)) {
                        ionScroll.attr(name, value);
                    }
                });

                return { pre: prelink };

                function prelink($scope, $element, $attr) {
                    var autoScrolling = false;
                    $scope.setActiveCamber = function (camber) {
                        $scope.activeCamber = camber;

                        var chats = $element[0].querySelector('.chat-scroll-wrapper').children,
                            len = chats.length,
                            temp = 0;
                        if (len === 0) {
                            return;
                        }
                        for (var i = 0; i < len; i++) {
                            if (chats[i].children[0].innerText === camber) {
                                break;
                            }
                        }
                        if (i === len) {
                            return;
                        }
                        var scrollCtrl = $ionicScrollDelegate.$getByHandle($attr.delegateHandle);
                        autoScrolling = true;
                        scrollCtrl.scrollTo(0, chats[i].offsetTop, true);
                        $timeout(function () {
                            autoScrolling = false;
                        }, 300);
                    };
                    $scope.onScroll = function () {
                        if (autoScrolling) {
                            return;
                        }
                        var scrollCtrl = $ionicScrollDelegate.$getByHandle($attr.delegateHandle),
                            pos = scrollCtrl.getScrollPosition(),
                            scrollView = scrollCtrl.getScrollView();
                        if (pos.top < 0 || pos.top > scrollView.__maxScrollTop) {
                            return;
                        }
                        var chats = $element[0].querySelector('.chat-scroll-wrapper').children,
                            len = chats.length,
                            temp = 0;
                        if (len === 0) {
                            return;
                        }
                        for (var i = 0; i < len; i++) {
                            if (chats[i].offsetTop > parseInt(pos.top)) {
                                temp = i - 1;
                                break;
                            }
                        }
                        if (i === 0) {
                            temp = 0;
                        } else if (i === len) {
                            temp = len - 1;
                        }
                        var camber = chats[temp].children[0].innerText;
                        if (camber !== $scope.activeCamber) {
                            $scope.$apply(function() {
                                $scope.activeCamber = camber;
                            });
                        }
                    };
                }
            }
        };
    }]);
    IonicU9Module.directive('chatGroup', function() {
        return {
            restrict: 'E',
            scope: {
                title: '@',
                class: '@'
            },
            transclude: true,
            replace: true,
            template:
            '<div class="chat-group" ng-class="class">' +
                '<div class="header">{{title}}</div>' +
                '<div class="body" ng-transclude></div>' +
            '</div>'
        };
    });

    IonicU9Module.service('debounce', ['$timeout', function ($timeout) {
        return function (func, wait, immediate, invokeApply) {
            var timeout, args, context, result;
            function debounce() {
                /* jshint validthis:true */
                context = this;
                args = arguments;
                var later = function () {
                    timeout = null;
                    if (!immediate) {
                        result = func.apply(context, args);
                    }
                };
                var callNow = immediate && !timeout;
                if (timeout) {
                    $timeout.cancel(timeout);
                }
                timeout = $timeout(later, wait, invokeApply);
                if (callNow) {
                    result = func.apply(context, args);
                }
                return result;
            }
            debounce.cancel = function () {
                $timeout.cancel(timeout);
                timeout = null;
            };
            return debounce;
        };
    }]);
    IonicU9Module.directive('debounce', ['debounce', '$parse', function (debounce, $parse) {
        return {
            require: 'ngModel',
            priority: 999,
            link: function ($scope, $element, $attrs, ngModelController) {
                var debounceDuration = $parse($attrs.debounce)($scope);
                var immediate = !!$parse($attrs.immediate)($scope);
                var debouncedValue, pass;
                var prevRender = ngModelController.$render.bind(ngModelController);
                var commitSoon = debounce(function (viewValue) {
                    pass = true;
                    ngModelController.$$lastCommittedViewValue = debouncedValue;
                    ngModelController.$setViewValue(viewValue);
                    pass = false;
                }, parseInt(debounceDuration, 10), immediate);
                ngModelController.$render = function () {
                    prevRender();
                    commitSoon.cancel();
                    //we must be first parser for this to work properly,
                    //so we have priority 999 so that we unshift into parsers last
                    debouncedValue = this.$viewValue;
                };
                ngModelController.$parsers.unshift(function (value) {
                    if (pass) {
                        debouncedValue = value;
                        return value;
                    } else {
                        commitSoon(ngModelController.$viewValue);
                        return debouncedValue;
                    }
                });
            }
        };
    }]);

    IonicU9Module.directive('searchBar', ['$ionicConfig', function($ionicConfig) {
        return {
            restrict: 'E',
            scope: {
                search: '='
            },
            replace: true,
            require: '?ngModel',
            transclude: true,
            template:
            '<div class="search-bar">' +
                '<label class="item-input-wrapper">' +
                    '<i class="icon ion-ios-search-strong placeholder-icon" ng-if="hasPlaceholderIcon"></i>' +
                    '<input type="text" ng-model="search">' +
                '</label>' +
                '<i class="icon ion-ios-close-empty" ng-if="search" ng-click="clearSearch()"></i>' +
            '</div>',
            compile: function(element, attr) {
                var input = element.find('input');
                angular.forEach({
                    'name': attr.name,
                    'placeholder': attr.placeholder,
                    'debounce': attr.debounce || '450',
                    'ng-disabled': attr.ngDisabled
                }, function(value, name) {
                    if (angular.isDefined(value)) {
                        input.attr(name, value);
                    }
                });
            },
            controller: ['$scope', '$element', '$attrs', function($scope, $element, $attrs) {
                $scope.hasPlaceholderIcon = angular.isDefined($attrs.hasPlaceholderIcon) ? !!$scope.hasPlaceholderIcon : true;

                $scope.clearSearch = function () {
                    $scope.search = '';
                };
            }]
        };
    }]);
})();